﻿using hyjx.comm;
using hyjx.comm.Exceptions;
using Hyjx.FeedingService.Configs;
using Hyjx.FeedingService.Objects;
using Hyjx.GanFengLogger;
using Hyjx.GF.Database;
using Microsoft.EntityFrameworkCore;
using S7.Net;
using Serilog;
using Serilog.Events;
using System.Collections.Concurrent;
using System.Drawing;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Unicode;

namespace Hyjx.FeedingService
{
    public class Feeding : BackgroundService
    {
        private string SerilogOutputTemplate = "[{Timestamp:yyy/MM/dd HH:mm:ss.fff} {Level:u3}] {Message:lj} {NewLine}{Exception}";

        readonly ILogger<Feeding>? _logger;
        readonly IConfiguration? _config;

        string? _dbConnectionString;
        AppHttp? _http;
        string? _webApiBaseUrl;
        string? _mixerRealStatus;
        string? _mixerSubmitStatus;
        string? _dilutionRealStatus;
        string? _dilutionSubmitStatus;
        bool _isPositive;
        int _heartBeatSeconds, _dilutionStepStatusChangedSeconds, _checkDilutionThirdStepStartSeconds, _dilutionStatusSeconds, _glueStepStatusChangedSeconds, _glueStatusSeconds, _factory;

        int _powderReportSeconds, _dilutionMaterialCode, _dilutionWaterCode;
        FeedingConfig[]? _feedings;
        GlueMaterialConfig[]? _glueMaterials;
        ConcurrentDictionary<string, S7Protocol> _protocols = new();

        readonly ConcurrentDictionary<string, int> _dilutionStepStates = new();
        readonly ConcurrentDictionary<string, int> _glueStepStates = new();
        readonly ConcurrentDictionary<string, bool> _heartbeats = new();
        readonly Dictionary<string, DateTime?> _dilutionThirdStepStartTimes = new(); // 稀释搅拌第三工步（全力搅拌）起始时间集，key = DeviceCode

        public Feeding(IConfiguration config, ILogger<Feeding> logger)
        {
            try
            {
                _config = config;
                _logger = logger;

                Log.Logger = new LoggerConfiguration()
                            .Enrich.FromLogContext()
                            .MinimumLevel.Debug()
                            .WriteTo.Logger(lg => lg.Filter.ByIncludingOnly(p =>
                            p.Level == LogEventLevel.Information).
                            WriteTo.File($"{AppDomain.CurrentDomain.BaseDirectory}/Log/{DateTime.Now:yyyyMM}/Info-.txt", rollingInterval: RollingInterval.Day,
                            rollOnFileSizeLimit: true,
                            fileSizeLimitBytes: 20_000_000,
                            retainedFileTimeLimit: TimeSpan.FromDays(90),
                            shared: true,
                            outputTemplate: SerilogOutputTemplate))
                            .WriteTo.Logger(lg => lg.Filter.ByIncludingOnly(p => p.Level == LogEventLevel.Error).WriteTo.File($"{AppDomain.CurrentDomain.BaseDirectory}/Log/{DateTime.Now:yyyyMM}/Error-.txt", rollingInterval: RollingInterval.Day, rollOnFileSizeLimit: true,
                            fileSizeLimitBytes: 20_000_000,
                            retainedFileTimeLimit: TimeSpan.FromDays(90),
                            shared: true,
                            outputTemplate: SerilogOutputTemplate))
                            .CreateLogger();
            }
            catch (Exception ex)
            {
                Log.Error(ex, "上料系统初始化失败");
            }
        }

        public override Task StartAsync(CancellationToken cancellationToken)
        {
            if (_config == null || _logger == null)
                return Task.CompletedTask;

            _webApiBaseUrl = _config.GetValue<string>("WebApiBaseUrl");
            _mixerRealStatus = _config.GetValue<string>("MixerRealStatus");
            _mixerSubmitStatus = _config.GetValue<string>("MixerSubmitStatus");
            _dilutionRealStatus = _config.GetValue<string>("DilutionRealStatus");
            _dilutionSubmitStatus = _config.GetValue<string>("DilutionSubmitStatus");
            _http = new(_logger);
            _isPositive = _config.GetValue<bool>("IsPositive");
            _heartBeatSeconds = _config.GetValue<int>("HeartBeatSeconds");
            _dilutionStepStatusChangedSeconds = _config.GetValue<int>("DilutionStepStatusChangedSeconds");
            _checkDilutionThirdStepStartSeconds = _config.GetValue<int>("CheckDilutionThirdStepStartSeconds");
            _dilutionStatusSeconds = _config.GetValue<int>("StatusSeconds");
            _glueStatusSeconds = _config.GetValue<int>("StatusSeconds");
            _glueStepStatusChangedSeconds = _config.GetValue<int>("GlueStepStatusChangedSeconds");
            _powderReportSeconds = _config.GetValue<int>("PowderReportSeconds");
            _feedings = _config.GetSection("Feedings").Get<FeedingConfig[]>();
            _glueMaterials = _config.GetSection("MaterialCode:Glue").Get<GlueMaterialConfig[]>();
            _dilutionMaterialCode = _config.GetValue<int>("DilutionMaterialCode");
            _dilutionWaterCode = _config.GetValue<int>("DilutionWaterCode");
            _factory = _config.GetValue<int>("Factory");

            IConfigurationRoot con = new ConfigurationBuilder()
                .SetBasePath(AppDomain.CurrentDomain.BaseDirectory)
                .AddJsonFile("DBsettings.json").Build();
            string? dbPath = con["GFDB:Path"];
            if (string.IsNullOrEmpty(dbPath))
            {
                Log.Error("读取数据库配置失败");
            }
            else
                _dbConnectionString = $"Filename={dbPath};Password={con["GFDB:Password"]}";

            if (_feedings == null)
                return Task.CompletedTask;

            foreach (var plc in _feedings.Where(p => p.IsEnabled))
            {
                _heartbeats.TryAdd(plc.Ip, false);
                TryAddToProtocolsAndConnect(plc, null);

                if (plc.RecipePoints == null)
                    continue;

                _glueStepStates.TryAdd(plc.GlueDeviceCode, 0);

                foreach (var item in plc.RecipePoints)
                {
                    if (string.IsNullOrEmpty(item.DeviceCode))
                        continue;

                    _dilutionThirdStepStartTimes.TryAdd(item.DeviceCode, null);
                    _dilutionStepStates.TryAdd(item.DeviceCode, 0);
                }
            }
            Log.Information("上料系统服务已启动");
            return base.StartAsync(cancellationToken);
        }

        public override Task StopAsync(CancellationToken cancellationToken)
        {
            _webApiBaseUrl = null;
            _http = null;
            _isPositive = true;
            _feedings = null;
            _glueMaterials = null;
            _heartBeatSeconds = 0;
            _dilutionStepStatusChangedSeconds = 0;
            _checkDilutionThirdStepStartSeconds = 0;
            _dilutionStatusSeconds = 0;
            _glueStatusSeconds = 0;
            _glueStepStatusChangedSeconds = 0;
            _dilutionMaterialCode = 0;
            _dilutionWaterCode = 0;
            _powderReportSeconds = 0;
            _factory = 0;
            _dbConnectionString = null;
            _protocols.Clear();
            _heartbeats.Clear();
            _dilutionThirdStepStartTimes.Clear();
            _glueStepStates.Clear();

            Log.Information("上料系统服务已停止");
            Log.CloseAndFlush();

            return base.StopAsync(cancellationToken);
        }

        S7Protocol TryAddToProtocolsAndConnect(FeedingConfig plc, S7Protocol? protocol)
        {
            if(protocol == null)
            {
                protocol = new(plc.CpuType, plc.Ip, plc.Rack, plc.Slot);
                Log.Information($"上料服务的 {plc.Ip} S7 协议已重新建立。");
            }

            try
            {
                _protocols.TryAdd(plc.Ip, protocol);
                if (!protocol.IsConnected)
                    protocol.Connect();
                else
                    Log.Information($"上料服务的 {plc.Ip} S7 协议已连接，不需要重连。");
            }
            catch (S7ProtocolConnectException ex)
            {
                Log.Error(ex, $"无法连接到PLC IP:{plc.Ip}");
            }
            return protocol;
        }

        protected override Task ExecuteAsync(CancellationToken stoppingToken)
        {
            int elapsedHeartBeatSecond = 1;
            int elapsedDilutionStepStatusChangedSecond = 1;
            int elapsedCheckDilutionThirdStepStartSeconds = 1;
            int elapsedDilutionStatus = 1;
            int elapsedPowderReportSeconds = 1;
            int elapsedGlueStepStatusChangedSecond = 1;
            int elapsedGlueStatus = 1;

            while (!stoppingToken.IsCancellationRequested)
            {
                if (elapsedHeartBeatSecond == _heartBeatSeconds)
                {
                    Task.Run(HeartBeat, stoppingToken);// 心跳
                    elapsedHeartBeatSecond = 1;
                }
                else
                    elapsedHeartBeatSecond++;

                if (elapsedPowderReportSeconds == _powderReportSeconds)
                {
                    Task.Run(CheckPowderReport, stoppingToken);// 粉体报表
                    elapsedPowderReportSeconds = 1;
                }
                else
                    elapsedPowderReportSeconds++;

                //M3的制胶机和稀释罐
                if (_factory == 3)
                {
                    if (elapsedCheckDilutionThirdStepStartSeconds == _checkDilutionThirdStepStartSeconds)
                    {
                        Task.Run(CheckDilutionThirdStepStartTime, stoppingToken);// 检测稀释搅拌第三工步（全力搅拌）开始时间
                        elapsedCheckDilutionThirdStepStartSeconds = 1;
                    }
                    else
                        elapsedCheckDilutionThirdStepStartSeconds++;


                    if (elapsedDilutionStepStatusChangedSecond == _dilutionStepStatusChangedSeconds)
                    {
                        Task.Run(DilutionStepStatusChanged, stoppingToken);// 稀释搅拌工步状态变更（225）
                        elapsedDilutionStepStatusChangedSecond = 1;
                    }
                    else
                        elapsedDilutionStepStatusChangedSecond++;

                    if (elapsedDilutionStatus == _dilutionStatusSeconds)
                    {
                        Task.Run(DilutionReportStatus, stoppingToken);// 稀释搅拌上报状态（227）
                        elapsedDilutionStatus = 1;
                    }
                    else
                        elapsedDilutionStatus++;

                    if (elapsedGlueStatus == _glueStatusSeconds)
                    {
                        Task.Run(ReportGlueStatus, stoppingToken); // 制胶机状态上报（207）
                        elapsedGlueStatus = 1;
                    }
                    else
                    {
                        elapsedGlueStatus++;
                    }

                    if (elapsedGlueStepStatusChangedSecond == _glueStepStatusChangedSeconds)
                    {
                        Task.Run(GlueStepStatusChanged, stoppingToken); // 制胶机状态变更上报（205）
                        elapsedGlueStepStatusChangedSecond = 1;
                    }
                    else
                    {
                        elapsedGlueStepStatusChangedSecond++;
                    }
                }

                Thread.Sleep(TimeSpan.FromSeconds(1));
            }

            return Task.CompletedTask;
        }

        private void GlueStepStatusChanged()// 制胶机工步状态变更（205）
        {
            if (_feedings == null || _http == null || string.IsNullOrEmpty(_webApiBaseUrl))
                return;

            FeedingConfig? config = _feedings.FirstOrDefault();
            if (config == null || config.GluePoints == null || string.IsNullOrEmpty(config.GluePoints.MesMode))
                return;

            foreach (var plc in _feedings.Where(p => p.IsEnabled))
            {
                Task.Run(() =>
                {
                    try
                    {
                        _protocols.TryGetValue(plc.Ip, out S7Protocol? feedingProtocol);
                        feedingProtocol = TryAddToProtocolsAndConnect(plc, feedingProtocol);

                        // 非 MES 模式下，不做上报
                        if (!PlcIsMesMode(feedingProtocol!, config.GluePoints.MesMode))
                            return;

                        var args = GetGlueStatus(config, feedingProtocol!);
                        if (args == null || args.Step <= 0)
                            return;

                        if (!_glueStepStates.TryGetValue(plc.GlueDeviceCode, out var state))
                        {
                            Log.Error($"工步状态变更异常：根据设备编码 {plc.GlueDeviceCode}，找不到内存里的工步状态字典。");
                            return;
                        }

                        if (state != args.StepStatus)
                        {
                            _http.SendGlueStatusChanged(_webApiBaseUrl + _mixerSubmitStatus, args); // 发送请求并记录日志
                            _glueStepStates[plc.GlueDeviceCode] = args.StepStatus;
                        }
                    }
                    catch (Exception ex)
                    {
                        Log.Error(ex, "读取制胶机工步状态改变点位出错。");
                    }
                });
            }
        }

        private void ReportGlueStatus()// 制胶机上报状态（207）
        {
            if (_feedings == null || _http == null || string.IsNullOrEmpty(_webApiBaseUrl))
                return;

            FeedingConfig? config = _feedings.FirstOrDefault();
            if (config == null || config.GluePoints == null || string.IsNullOrEmpty(config.GluePoints.MesMode))
                return;

            foreach (var plc in _feedings.Where(p => p.IsEnabled))
            {
                Task.Run(() =>
                {
                    try
                    {
                        _protocols.TryGetValue(plc.Ip, out S7Protocol? feedingProtocol);
                        feedingProtocol = TryAddToProtocolsAndConnect(plc, feedingProtocol);

                        // 非 MES 模式下，不做上报
                        if (!PlcIsMesMode(feedingProtocol!, config.GluePoints.MesMode))
                            return;

                        var args = GetGlueStatus(config, feedingProtocol!);
                        if (args != null && args.Step > 0)
                            _http.SendGlueStatus(_webApiBaseUrl + _mixerRealStatus, args);
                    }
                    catch (Exception ex)
                    {
                        Log.Error(ex, "读取制胶机状态点位出错。");
                    }
                });
            }
        }

        void CheckPowderReport() // 粉体报表数据检测
        {
            if (_feedings == null || string.IsNullOrEmpty(_dbConnectionString) || _logger == null)
                return;

            foreach (var plc in _feedings.Where(p => p.IsEnabled))
            {
                if (plc.PowderReport == null || plc.PowderReport.Address == null || plc.PowderReport.RelatedMixers == null)
                    continue;

                PowderReportAddressConfig address = plc.PowderReport.Address;

                foreach (var mixer in plc.PowderReport.RelatedMixers)
                {
                    Task.Run(() =>
                    {
                        _protocols.TryGetValue(plc.Ip, out S7Protocol? feedingProtocol);
                        feedingProtocol = TryAddToProtocolsAndConnect(plc, feedingProtocol);

                        string boolAddress = $"DB{mixer.DbNumber}.DBX{address.StartFeedingPointAddress}";
                        try
                        {
                            bool isStartFeeding = feedingProtocol.ReadBool(boolAddress);
                            if (isStartFeeding) // 开始上料
                            {
                                string materialAdd = $"DB{mixer.DbNumber}.DBW{address.MaterialAddress}";
                                int materialCode = feedingProtocol.ReadInt(materialAdd);

                                Log.Information($"开始上料点位:{boolAddress},值:{isStartFeeding},物料编码:{materialCode}");
                                using GFDbContext db = new(_dbConnectionString);
                                // 如果没有一条有开始没结束的上料记录，则记一条新的
                                if (!db.PowderReports.Any(p => p.DeviceName == mixer.Name && p.MaterialCode == materialCode && p.EndTime == null && p.ActualValue == null))
                                {
                                    // 记录上料时间，物料编码，设定值
                                    DateTime now = DateTime.Now;
                                    PowderReportEntity report = new()
                                    {
                                        StartTime = now,
                                        CreatedTime = now,
                                        DeviceName = mixer.Name,
                                        SettingValue = (decimal)feedingProtocol.ReadFloat($"DB{mixer.DbNumber}.DBD{address.SettingValueAddress}"),
                                        MaterialCode = materialCode,
                                        EndTime = null
                                    };
                                    db.PowderReports.Add(report);
                                    db.SaveChanges();

                                    Log.Information(JsonSerializer.Serialize(report, new JsonSerializerOptions()
                                    {
                                        Encoder = JavaScriptEncoder.Create(UnicodeRanges.BasicLatin, UnicodeRanges.CjkUnifiedIdeographs, UnicodeRanges.CjkUnifiedIdeographsExtensionA)
                                    }));
                                }
                            }
                            else
                            {
                                string bAddress = $"DB{mixer.DbNumber}.DBX{address.EndFeedingPointAddress}";
                                bool isEndFeeding = feedingProtocol.ReadBool(bAddress);
                                if (isEndFeeding)// 上料完成
                                {
                                    string materialAdd = $"DB{mixer.DbNumber}.DBW{address.MaterialAddress}";
                                    int materialCode = feedingProtocol.ReadInt(materialAdd);
                                    Log.Information($"结束上料点位:{bAddress},值:{isEndFeeding},物料编码:{materialCode}");

                                    using GFDbContext db = new(_dbConnectionString);
                                    PowderReportEntity? report = db.PowderReports.FirstOrDefault(p => p.DeviceName == mixer.Name && p.MaterialCode == materialCode && p.EndTime == null);
                                    if (report == null)
                                        return;

                                    report.EndTime = DateTime.Now;
                                    report.ActualValue = (decimal)feedingProtocol.ReadFloat($"DB{mixer.DbNumber}.DBD{address.ActualValueAddress}");

                                    db.SaveChanges();

                                    Log.Information(JsonSerializer.Serialize(report, new JsonSerializerOptions()
                                    {
                                        Encoder = JavaScriptEncoder.Create(UnicodeRanges.BasicLatin, UnicodeRanges.CjkUnifiedIdeographs, UnicodeRanges.CjkUnifiedIdeographsExtensionA)
                                    }));
                                }
                            }

                        }
                        catch (S7ProtocolReadBoolException ex)
                        {
                            Log.Error(ex, "读取粉体报表布尔点位出错。");
                        }
                        catch (S7ProtocolReadIntException ex)
                        {
                            Log.Error(ex, "读取粉体报表整型点位出错。");
                        }
                        catch (S7ProtocolReadFloatException ex)
                        {
                            Log.Error(ex, "读取粉体报表浮点型点位出错。");
                        }
                        catch (DbUpdateConcurrencyException ex)
                        {
                            Log.Error(ex, "粉体报表写入数据库出错。");
                        }
                        catch (Exception ex)
                        {
                            Log.Error(ex, "粉体报表出错。");
                        }
                    });
                }
            }
        }

        private void DilutionReportStatus()// 稀释搅拌上报状态（227）
        {
            if (_feedings == null || string.IsNullOrEmpty(_webApiBaseUrl) || _http == null)
                return;

            foreach (var plc in _feedings.Where(p => p.IsEnabled))
            {
                if (plc.RecipePoints == null)
                    continue;

                foreach (var point in plc.RecipePoints)
                {
                    Task.Run(() =>
                    {
                        try
                        {
                            _protocols.TryGetValue(plc.Ip, out S7Protocol? feedingProtocol);
                            feedingProtocol = TryAddToProtocolsAndConnect(plc, feedingProtocol);

                            ReportStatusArgs? args = GetDilutionStatus(point, feedingProtocol!);
                            if(args != null)
                                Log.Information($"上料系统 {point.DeviceCode} 227 上报状态时获取的参数：{JsonSerializer.Serialize(args)}");
                            else
                                Log.Information($"上料系统 {point.DeviceCode} 227 上报状态时获取的参数为空，");

                            if (args == null || args.Step < 1)
                                return;

                            _http.SendDilutionStatus(_webApiBaseUrl + _dilutionRealStatus, args);
                        }
                        catch (Exception ex)
                        {
                            Log.Error(ex, $"上料系统 {plc.Ip} 稀释搅拌状态上报失败");
                        }
                    });
                }
            }
        }

        private void CheckDilutionThirdStepStartTime()// 检测稀释搅拌第三工步（全力搅拌）开始时间
        {
            if (_feedings == null || _logger == null)
                return;

            foreach (var plc in _feedings.Where(p => p.IsEnabled))
            {
                if (plc.RecipePoints == null)
                    continue;

                foreach (var point in plc.RecipePoints)
                {
                    if (string.IsNullOrEmpty(point.DeviceCode))
                        continue;

                    Task.Run(() =>
                    {
                        try
                        {
                            _protocols.TryGetValue(plc.Ip, out S7Protocol? feedingProtocol);
                            feedingProtocol = TryAddToProtocolsAndConnect(plc, feedingProtocol);

                            int step = feedingProtocol!.ReadInt(point.StepAddress);
                            if (step == 400 && _dilutionThirdStepStartTimes[point.DeviceCode] != null) // 如果进入全力搅拌状态
                                _dilutionThirdStepStartTimes[point.DeviceCode] = DateTime.Now;
                            else if (step == 500)
                                _dilutionThirdStepStartTimes[point.DeviceCode] = null; // 搅拌完成，将搅拌起始时间置空
                        }
                        catch (Exception ex)
                        {
                            Log.Error(ex, "获取稀释全力搅拌工步运行起始时间异常。");
                        }
                    });
                }
            }
        }

        private void DilutionStepStatusChanged()// 稀释搅拌工步状态变更（225）
        {
            if (_feedings == null || string.IsNullOrEmpty(_webApiBaseUrl) || _http == null)
                return;

            foreach (var plc in _feedings.Where(p => p.IsEnabled))
            {
                if (plc.RecipePoints == null)
                    continue;

                foreach (var item in plc.RecipePoints)
                {
                    if (string.IsNullOrEmpty(item.DeviceCode))
                        continue;

                    Task.Run(() =>
                    {
                        try
                        {
                            _protocols.TryGetValue(plc.Ip, out S7Protocol? feedingProtocol);
                            feedingProtocol = TryAddToProtocolsAndConnect(plc, feedingProtocol);

                            ReportStatusArgs? args = GetDilutionStatus(item, feedingProtocol!);
                            if (args != null)
                                Log.Information($"稀释搅拌 {item.DeviceCode} 225 上报工步状态变更时获取的参数：{JsonSerializer.Serialize(args)}");
                            else
                                Log.Information($"稀释搅拌 {item.DeviceCode} 225 上报工步状态变更时的参数为空，");

                            if (args == null || args.Step <= 0 || args.StepStatus == 0)
                                return;

                            if (!_dilutionStepStates.TryGetValue(item.DeviceCode, out var state))
                            {
                                Log.Error($"工步状态变更异常：根据设备编码 {item.DeviceCode}，找不到内存里的工步状态字典。");
                                return;
                            }
                            //bool getStep = _dilutionSteps.TryGetValue(item.DeviceCode, out int step);
                            if (args.StepStatus != state)
                            {
                                _http.SendDilutionStatusChanged(_webApiBaseUrl + _dilutionSubmitStatus, args); // 发送请求并记录日志
                                _dilutionStepStates[item.DeviceCode] = args.StepStatus;
                                //_dilutionSteps[item.DeviceCode] = args.Step;
                            }
                        }
                        catch (Exception ex)
                        {
                            Log.Error(ex, $"上料系统 {plc.Ip} 稀释搅拌工步状态变更上报失败");
                        }
                    });
                }
            }
        }

        private void HeartBeat()
        {
            if (_feedings == null)
                return;

            foreach (var plc in _feedings.Where(p => p.IsEnabled))
            {
                Task.Run(() =>
                {
                    try
                    {
                        if (!_heartbeats.TryGetValue(plc.Ip, out var heart))
                        {
                            Log.Error($"心跳异常：根据设备IP {plc.Ip}，找不到内存里的心跳字典。");
                            return;
                        }

                        _protocols.TryGetValue(plc.Ip, out S7Protocol? feedingProtocol);
                        feedingProtocol = TryAddToProtocolsAndConnect(plc, feedingProtocol);

                        if (feedingProtocol.IsConnected)
                        {
                            feedingProtocol.Write(plc.HeartBeatAddress, heart);
                            _heartbeats[plc.Ip] = !heart;
                        }
                    }
                    catch (Exception ex)
                    {
                        Log.Error(ex, $"上料系统 {plc.Ip} 心跳维持失败");
                    }
                });
            }
        }

        bool _dilutionForthStepIsOver = false;

        ReportStatusArgs? GetDilutionStatus(RecipePointConfig point, S7Protocol s7)
        {
            if (_config == null || _logger == null)
                return null;

            if (string.IsNullOrEmpty(point.ActualTemperatureAddress) || string.IsNullOrEmpty(point.ActualVacuumAddress)
                || string.IsNullOrEmpty(point.CmdAddress) || string.IsNullOrEmpty(point.ElapsedTimeAddress)
                || string.IsNullOrEmpty(point.MaterialActualWeightAddress) || string.IsNullOrEmpty(point.MaterialWeightAddress)
                || string.IsNullOrEmpty(point.StepAddress) || string.IsNullOrEmpty(point.StirRotateActualSpeedAddress)
                || string.IsNullOrEmpty(point.StirRotateSpeedAddress) || string.IsNullOrEmpty(point.VacuumAddress)
                || string.IsNullOrEmpty(point.WaterWeightAddress))
            {
                return null;
            }

            try
            {
                Log.Information($"稀释搅拌 {point.DeviceCode} 进入获取稀释搅拌状态方法：{nameof(GetDilutionStatus)}");

                int step = s7.ReadInt(point.StepAddress); // 内部工步号
                int stepNumber = 0; // 工步号
                float materialWeight = 0; // 物料重量
                int stepStatus = 1; // 工步状态进行中
                bool recipeStep1Start = false;
                bool recipeStep2Start = false;

                string folder = $"{Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData)}\\hyjx";
                string pathStart1 = $"{folder}\\{point.DeviceCode}_recipe_step1_start";
                string pathStart2 = $"{folder}\\{point.DeviceCode}_recipe_step2_start";
                if (File.Exists(pathStart1))
                {
                    recipeStep1Start = true;
                }

                if (File.Exists(pathStart2))
                {
                    recipeStep2Start = true;
                }

                if (step >= 1 && step < 300 && recipeStep1Start)
                {
                    stepNumber = 1;
                    materialWeight = s7.ReadFloat(point.MaterialWeightAddress);
                }
                else if (step > 200 && step < 400)
                {
                    stepNumber = 2;
                    materialWeight = s7.ReadFloat(point.WaterWeightAddress);
                    if (File.Exists(pathStart1))
                    {
                        File.Delete(pathStart1); // 删除文件
                    }
                }
                else if (step >= 400 && step < 600) // 搅拌工步
                    stepNumber = 3;
                //else if (step >= 600 && step <= 799)
                //    stepNumber = 4;

                // 第一步已完成
                if (step == 200)
                {
                    stepNumber = 1;
                    stepStatus = 2;
                }
                else if (step == 350 && recipeStep2Start)// 第二步已完成
                {
                    stepNumber = 2;
                    stepStatus = 2;
                }
                else if (step == 600)// 第三步已完成
                {
                    stepNumber = 3;
                    stepStatus = 2;

                    if (File.Exists(pathStart2))
                    {
                        File.Delete(pathStart2); // 删除文件
                    }
                }

                if (stepNumber == 4 && _dilutionForthStepIsOver)
                {
                    stepNumber = 0;
                    stepStatus = 0;
                    _dilutionForthStepIsOver = false;
                }

                // 223（稀释搅拌工步完成）接口会写入 DeviceCode_recipe_over 文件
                //string path223 = $"{folder}\\{point.DeviceCode}_recipe_over";
                //if (File.Exists(path223))
                //{
                //    if (stepNumber == 4 && !_dilutionForthStepIsOver)
                //    {
                //        stepStatus = 2; // 第四步完成
                //        File.Delete(path223); // 删除文件
                //        _dilutionForthStepIsOver = true; // 第四工步完成设为 true
                //    }
                //}

                ReportStatusArgs args = new()
                {
                    ActualVacuum = (float)Math.Round(s7.ReadFloat(point.ActualVacuumAddress) / 1000, 3), // Kpa 要转接口 Mpa
                    ActualTemperature = s7.ReadFloat(point.ActualTemperatureAddress),
                    DeviceCode = string.IsNullOrEmpty(point.DeviceCode) ? string.Empty : point.DeviceCode,
                    ElapsedTime = s7.ReadInt(point.ElapsedTimeAddress),
                    MaterialActualWeight = s7.ReadFloat(point.MaterialActualWeightAddress),
                    Step = stepNumber,
                    MaterialWeight = materialWeight,
                    StepStatus = stepStatus,
                    StirRotateActualSpeed = s7.ReadInt(point.StirRotateActualSpeedAddress),
                    StirRotateSpeed = s7.ReadInt(point.StirRotateSpeedAddress),
                    Temperature = -1,
                    Vacuum = (float)Math.Round(s7.ReadFloat(point.VacuumAddress) / 1000, 3), // Kpa 要转接口 Mpa
                    Status = s7.ReadInt(point.EquipmentStatus) switch
                    {
                        1 => "运行",
                        2 => "待机",
                        3 => "故障",
                        _ => throw new ArgumentOutOfRangeException("EquipmentStatus")
                    },
                };
                if (stepNumber == 1)
                    args.MaterialCode = _dilutionMaterialCode;
                else if (stepNumber == 2)
                    args.MaterialCode = _dilutionWaterCode;
                else
                    args.MaterialCode = 0;

                if (stepNumber == 3 && !string.IsNullOrEmpty(point.DeviceCode) && _dilutionThirdStepStartTimes[point.DeviceCode] != null)
                {
                    var span = DateTime.Now - _dilutionThirdStepStartTimes[point.DeviceCode];
                    if (span != null)
                        args.RemainTime = (int)(args.ElapsedTime - span.Value.TotalMinutes);
                }

                return args;
            }
            catch (S7ProtocolException ex)
            {
                Log.Error(ex, "获取稀释搅拌机状态点位异常。");
                return null;
            }
            catch (Exception ex)
            {
                Log.Error(ex, "获取稀释搅拌机状态未知异常。");
                return null;
            }
        }

        bool PlcIsMesMode(S7Protocol protocol, string addr)
        {
            try
            {
                bool isMesMode = protocol.ReadBool(addr);
                return isMesMode;
            }
            catch (Exception err)
            {
                Log.Error(err, "读取MES模式异常");
                return false;
            }
        }

        ReportMixerStatusArgs? GetGlueStatus(FeedingConfig config, S7Protocol plc)
        {
            if (_glueMaterials == null)
                throw new Exception(nameof(_glueMaterials));
            try
            {
                if (config.GluePoints == null)
                    throw new ArgumentNullException(nameof(config.GluePoints));

                string deviceCode = config.GlueDeviceCode;
                byte[] statusBytes = plc.ReadBytes(config.GluePoints.StatusDb, config.GluePoints.StatusStartPoint, config.GluePoints.StatusLength);
                byte[] realtimeBytes = plc.ReadBytes(config.GluePoints.StepRealtimeDb, config.GluePoints.StepRealtimeStartPoint, config.GluePoints.StepRealtimeLength);
                List<bool> bools = realtimeBytes[74].FromByteTo8Bool();
                short materialCode = new byte[] { realtimeBytes[36], realtimeBytes[37] }.FromBytesToShort();
                var material = _glueMaterials.FirstOrDefault(p => p.PowderPlcValue == materialCode);

                ReportMixerStatusArgs args = new()
                {
                    DeviceCode = deviceCode,
                    ScatteredRotateSpeed = (short)new byte[] { realtimeBytes[66], realtimeBytes[67], realtimeBytes[68], realtimeBytes[69] }.FromBytesToFloat(),
                    ScatteredRotateActualSpeed = (float)Math.Round(new byte[] { statusBytes[28], statusBytes[29], statusBytes[30], statusBytes[31] }.FromBytesToFloat(), 3),
                    StirRotateSpeed = (short)new byte[] { realtimeBytes[62], realtimeBytes[63], realtimeBytes[64], realtimeBytes[65] }.FromBytesToFloat(),
                    StirRotateActualSpeed = (float)Math.Round(new byte[] { statusBytes[24], statusBytes[25], statusBytes[26], statusBytes[27] }.FromBytesToFloat(), 3),
                    ElapsedTime = new byte[] { realtimeBytes[70], realtimeBytes[71] }.FromBytesToShort(),
                    RemainTime = new byte[] { realtimeBytes[72], realtimeBytes[73] }.FromBytesToShort(),
                    ActualTemperature = (float)Math.Round(new byte[] { statusBytes[8], statusBytes[9], statusBytes[10], statusBytes[11] }.FromBytesToFloat(), 3),
                    ActualVacuum = (float)Math.Round(new byte[] { statusBytes[4], statusBytes[5], statusBytes[6], statusBytes[7] }.FromBytesToFloat(), 3) / 1000,
                    StirCurrent = (float)Math.Round(new byte[] { statusBytes[12], statusBytes[13], statusBytes[14], statusBytes[15] }.FromBytesToFloat(), 3),
                    ScatteredCurrent = (float)Math.Round(new byte[] { statusBytes[16], statusBytes[17], statusBytes[18], statusBytes[19] }.FromBytesToFloat(), 3),
                    Temperature = (float)Math.Round(new byte[] { realtimeBytes[46], realtimeBytes[47], realtimeBytes[48], realtimeBytes[49] }.FromBytesToFloat(), 3),
                    Vacuum = (float)Math.Round(new byte[] { realtimeBytes[54], realtimeBytes[55], realtimeBytes[56], realtimeBytes[57] }.FromBytesToFloat(), 3) / 1000, // Kpa 要转接口 Mpa
                    StepStatus = bools[4] == true ? 2 : 1,
                    RecipeRunning = true,
                    MixerDischarge = false,
                    EcMesValid = false,
                    MesMode = plc.ReadBool(config.GluePoints.MesMode),
                    Step = new byte[] { realtimeBytes[0], realtimeBytes[1] }.FromBytesToShort(),
                    MaterialCode = material == null ? (short)0 : (short)material.GanFengValue,
                    MaterialWeight = (float)Math.Round(new byte[] { realtimeBytes[38], realtimeBytes[39], realtimeBytes[40], realtimeBytes[41] }.FromBytesToFloat(), 3),
                    MaterialActualWeight = (float)Math.Round(new byte[] { realtimeBytes[42], realtimeBytes[43], realtimeBytes[44], realtimeBytes[45] }.FromBytesToFloat(), 3),
                    Status = plc.ReadInt(config.GluePoints.EquipmentStatus) switch
                    {
                        1 => "运行",
                        2 => "待机",
                        3 => "故障",
                        _ => throw new ArgumentOutOfRangeException("EquipmentStatus")
                    }
                };

                return args;
            }
            catch (S7ProtocolReadException ex)
            {
                Log.Error(ex, "读取制胶机 PLC 过程数据出错");
                return null;
            }
            catch (Exception ex)
            {
                Log.Error(ex, "制胶机 PLC 过程数据异常");
                return null;
            }
        }
    }
}
